﻿
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using GRTVPluginConsole.Webserver.Compiler;
using System.Collections.Generic;

namespace GRTVPluginConsole.Webserver
{
    class TinyWebServer
    {
        private bool isListening;
        private TcpListener myListener;
        private string webPath;
        //The constructor which make the TcpListener start listening on th
        //given port. It also calls a Thread on the method StartListen(). 
        public TinyWebServer(int port, string webPath)
        {
            this.webPath = webPath;
            try
            {
                isListening = true;
                //start listing on the given port
                myListener = new TcpListener(IPAddress.Any, port);
                myListener.Start();
                Console.WriteLine("Web Server Running... Press ^C to Stop...");

                //start the thread which calls the method 'StartListen'
                Thread th = new Thread(new ThreadStart(StartListen));
                th.Start();

            }
            catch (Exception e)
            {
                Console.WriteLine("An Exception Occurred while Listening :"
                                   + e.ToString());
            }
        }

        public void StopListener()
        {
            isListening = false;
            myListener.Stop();
        }

        public string GetTheDefaultFileName(string sLocalDirectory)
        {
            StreamReader sr;
            String sLine = "";

            try
            {
                //Open the default.dat to find out the list
                // of default file
                sr = new StreamReader(webPath + "/Default.Dat");

                while ((sLine = sr.ReadLine()) != null)
                {
                    //Look for the default file in the web server root folder
                    if (File.Exists(sLocalDirectory + sLine) == true)
                        break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("An Exception Occurred : " + e.ToString());
            }
            if (File.Exists(sLocalDirectory + sLine) == true)
                return sLine;
            else
                return "";
        }

        public string GetLocalPath(string sMyWebServerRoot, string sDirName)
        {
            if (true) return sMyWebServerRoot + sDirName;

            StreamReader sr;
            String sLine = "";
            String sVirtualDir = "";
            String sRealDir = "";
            int iStartPos = 0;


            //Remove extra spaces
            sDirName.Trim();



            // Convert to lowercase
            sMyWebServerRoot = sMyWebServerRoot.ToLower();

            // Convert to lowercase
            sDirName = sDirName.ToLower();


            try
            {
                //Open the Vdirs.dat to find out the list virtual directories
                sr = new StreamReader(webPath + "/VDirs.Dat");

                while ((sLine = sr.ReadLine()) != null)
                {
                    //Remove extra Spaces
                    sLine.Trim();

                    if (sLine.Length > 0)
                    {
                        //find the separator
                        iStartPos = sLine.IndexOf(";");

                        // Convert to lowercase
                        sLine = sLine.ToLower();

                        sVirtualDir = sLine.Substring(0, iStartPos);
                        sRealDir = sLine.Substring(iStartPos + 1);

                        if (sVirtualDir == sDirName)
                        {
                            break;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("An Exception Occurred : " + e.ToString());
            }


            if (sVirtualDir == sDirName)
                return sRealDir;
            else
                return "";
        }

        public string GetMimeType(string sRequestedFile)
        {


            StreamReader sr;
            String sLine = "";
            String sMimeType = "";
            String sFileExt = "";
            String sMimeExt = "";

            // Convert to lowercase
            sRequestedFile = sRequestedFile.ToLower();

            int iStartPos = sRequestedFile.IndexOf(".");

            sFileExt = sRequestedFile.Substring(iStartPos);

            try
            {
                //Open the Vdirs.dat to find out the list virtual directories
                sr = new StreamReader(webPath + "/Mime.Dat");

                while ((sLine = sr.ReadLine()) != null)
                {

                    sLine.Trim();

                    if (sLine.Length > 0)
                    {
                        //find the separator
                        iStartPos = sLine.IndexOf(";");

                        // Convert to lower case
                        sLine = sLine.ToLower();

                        sMimeExt = sLine.Substring(0, iStartPos);
                        sMimeType = sLine.Substring(iStartPos + 1);

                        if (sMimeExt == sFileExt)
                            break;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("An Exception Occurred : " + e.ToString());
            }

            if (sMimeExt == sFileExt)
                return sMimeType;
            else
                return "";
        }

        public void SendHeader(string sHttpVersion, string sMIMEHeader,
            int iTotBytes, string sStatusCode, ref Socket mySocket)
        {

            String sBuffer = "";

            // if Mime type is not provided set default to text/html
            if (sMIMEHeader.Length == 0)
            {
                sMIMEHeader = "text/html";  // Default Mime Type is text/html
            }

            sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n";
            sBuffer = sBuffer + "Server: cx1193719-b\r\n";
            sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + "\r\n";
            sBuffer = sBuffer + "Accept-Ranges: bytes\r\n";
            sBuffer = sBuffer + "Content-Length: " + iTotBytes + "\r\n\r\n";

            Byte[] bSendData = Encoding.ASCII.GetBytes(sBuffer);

            SendToBrowser(bSendData, ref mySocket);

            Console.WriteLine("Total Bytes : " + iTotBytes.ToString());

        }

        public void SendToBrowser(String sData, ref Socket mySocket)
        {
            SendToBrowser(Encoding.ASCII.GetBytes(sData), ref mySocket);
        }


        public void SendToBrowser(Byte[] bSendData, ref Socket mySocket)
        {
            int numBytes = 0;
            try
            {
                if (mySocket.Connected)
                {
                    if ((numBytes = mySocket.Send(bSendData,
                          bSendData.Length, 0)) == -1)
                        Console.WriteLine("Socket Error cannot Send Packet");
                    else
                    {
                        Console.WriteLine("No. of bytes send {0}", numBytes);
                    }
                }
                else
                    Console.WriteLine("Connection Dropped....");
            }
            catch (Exception e)
            {
                Console.WriteLine("Error Occurred : {0} ", e);
            }
        }

        public void StartListen()
        {

            int iStartPos = 0;
            String sRequest;
            String sDirName;
            String sRequestedFile;
            String sErrorMessage;
            String sLocalDir;
            String sMyWebServerRoot = webPath + "/www/";
            String sPhysicalFilePath = "";
            String sFormattedMessage = "";
            String sResponse = "";
            String sParameters = "";
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            HttpParameters httpParameters = new HttpParameters(parameters);
            while (isListening)
            {
                //Accept a new connection
                try
                {
                    if (!isListening) {
                        return;
                    }
                    Socket mySocket = myListener.AcceptSocket();
                    Console.WriteLine("Socket Type " + mySocket.SocketType);
                    if (mySocket.Connected)
                    {
                        Console.WriteLine("\nClient Connected!!\n==================\nCLient IP {0}\n", mySocket.RemoteEndPoint);


                        //make a byte array and receive data from the client 
                        Byte[] bReceive = new Byte[1024];
                        int i = mySocket.Receive(bReceive, bReceive.Length, 0);


                        //Convert Byte to String
                        string sBuffer = Encoding.ASCII.GetString(bReceive);


                        //At present we will only deal with GET type
                        if (sBuffer.Substring(0, 3) != "GET")
                        {
                            Console.WriteLine("Only Get Method is supported..");
                            mySocket.Close();
                            continue;
                        }

                        iStartPos = sBuffer.IndexOf("Referer:");
                        string referer = "";
                        if (iStartPos > 0)
                        {
                            int end = sBuffer.IndexOf("\r\n", iStartPos);
                            referer = sBuffer.Substring(iStartPos + 9, end - iStartPos - 9).Trim();
                        }
                        // Look for HTTP request
                        iStartPos = sBuffer.IndexOf("HTTP", 1);


                        // Get the HTTP text and version e.g. it will return "HTTP/1.1"
                        string sHttpVersion = sBuffer.Substring(iStartPos, 8);


                        // Extract the Requested Type and Requested file/directory
                        sRequest = sBuffer.Substring(0, iStartPos - 1);


                        //Replace backslash with Forward Slash, if Any
                        sRequest.Replace("\\", "/");

                        iStartPos = sRequest.LastIndexOf("?");
                        if (iStartPos != -1)
                        {
                            sParameters = sRequest.Substring(iStartPos + 1);
                            string[] strParams = sParameters.Split('&');
                            for (int j = 0; j < strParams.Length; j++)
                            {
                                int iv = strParams[j].IndexOf("=");
                                if (iv == -1)
                                {
                                    parameters[strParams[j]] = "";
                                }
                                else
                                {
                                    parameters[strParams[j].Substring(0, iv)] = strParams[j].Substring(iv + 1);
                                }
                            }
                            sRequest = sRequest.Substring(0, iStartPos);
                        }
                        //If file name is not supplied add forward slash to indicate 
                        //that it is a directory and then we will look for the 
                        //default file name..
                        if ((sRequest.IndexOf(".") < 1) && (!sRequest.EndsWith("/")))
                        {
                            sRequest = sRequest + "/";
                        }
                        //Extract the requested file name
                        iStartPos = sRequest.LastIndexOf("/") + 1;
                        sRequestedFile = sRequest.Substring(iStartPos);


                        //Extract The directory Name
                        sDirName = sRequest.Substring(sRequest.IndexOf("/"),
                                   sRequest.LastIndexOf("/") - 3);

                        /////////////////////////////////////////////////////////////////////
                        // Identify the Physical Directory
                        /////////////////////////////////////////////////////////////////////
                        if (sDirName.StartsWith("/cmd/ViewDriver/"))
                        {
                            int number = 0;
                            Int32.TryParse(parameters["driver"], out number);
                            int slotId = number;

                            number = -1;
                            Int32.TryParse(parameters["camera"], out number);
                            int cameraIndex = number;

                            mySocket.Close();

                            Program.ViewDriver(slotId, cameraIndex);
                            continue;
                        }
                        else if (sDirName.StartsWith("/cmd/WidgetVisibility/"))
                        {
                            int number = 0;
                            Int32.TryParse(parameters["visible"], out number);
                            byte vis = (byte)number;

                            number = -1;
                            Int32.TryParse(parameters["widgetIndex"], out number);
                            byte widgetIndex = (byte)number;

                            mySocket.Close();

                            Program.sendWidgetVisibility(widgetIndex, vis);
                            continue;
                        }
                        else if (sDirName.StartsWith("/cmd/BaseCommand/"))
                        {
                            int number = 0;
                            Int32.TryParse(parameters["type"], out number);
                            byte cmdType = (byte)number;

                            number = -1;
                            Int32.TryParse(parameters["value"], out number);
                            byte cmdValue = (byte)number;

                            mySocket.Close();
                            Program.sendPluginCommand(cmdType, cmdValue);
                            continue;
                        }
                        else if (sDirName.StartsWith("/cmd/GoLive/"))
                        {
                            mySocket.Close();
                            Program.sendGoLive();
                            continue;
                        }
                        else if (sDirName.StartsWith("/cmd/ReplayEvent/"))
                        {
                            string uuid = httpParameters.GetString("uuid", "");
                            int seconds = httpParameters.GetInt("seconds", 6);
                            int cameraIndex = httpParameters.GetInt("camera", -1);

                            mySocket.Close();
                            Program.sendReplayEvent(uuid, seconds, cameraIndex);
                            continue;
                        }
                        else if (sDirName == "/")
                        {
                            sLocalDir = sMyWebServerRoot;
                        }
                        else
                        {
                            //Get the Virtual Directory
                            sLocalDir = GetLocalPath(sMyWebServerRoot, sDirName);
                        }


                        Console.WriteLine("Directory Requested : " + sLocalDir);

                        //If the physical directory does not exists then
                        // dispaly the error message
                        if (sLocalDir.Length == 0)
                        {
                            sErrorMessage = "<H2>Error!! Requested Directory does not exists</H2><Br>";
                            //sErrorMessage = sErrorMessage + "Please check data\\Vdirs.Dat";

                            //Format The Message
                            SendHeader(sHttpVersion, "", sErrorMessage.Length,
                                       " 404 Not Found", ref mySocket);

                            //Send to the browser
                            SendToBrowser(sErrorMessage, ref mySocket);

                            mySocket.Close();

                            continue;
                        }

                        /////////////////////////////////////////////////////////////////////
                        // Identify the File Name
                        /////////////////////////////////////////////////////////////////////

                        //If The file name is not supplied then look in the default file list
                        if (sRequestedFile.Length == 0)
                        {
                            // Get the default filename
                            sRequestedFile = GetTheDefaultFileName(sLocalDir);

                            if (sRequestedFile == "")
                            {
                                sErrorMessage = "<H2>Error!! No Default File Name Specified</H2>";
                                SendHeader(sHttpVersion, "", sErrorMessage.Length,
                                           " 404 Not Found", ref mySocket);
                                SendToBrowser(sErrorMessage, ref mySocket);

                                mySocket.Close();

                                continue;

                            }
                        }

                        //////////////////////////////////////////////////
                        // Get TheMime Type
                        //////////////////////////////////////////////////

                        String sMimeType = GetMimeType(sRequestedFile);


                        //Build the physical path
                        sPhysicalFilePath = sLocalDir + sRequestedFile;
                        Console.WriteLine("File Requested : " + sPhysicalFilePath);

                        if (File.Exists(sPhysicalFilePath) == false)
                        {

                            sErrorMessage = "<H2>404 Error! File Does Not Exists...</H2>";
                            SendHeader(sHttpVersion, "", sErrorMessage.Length,
                                       " 404 Not Found", ref mySocket);
                            SendToBrowser(sErrorMessage, ref mySocket);

                            Console.WriteLine(sFormattedMessage);
                        }
                        else
                        {
                            int iTotBytes = 0;

                            sResponse = "";



                            if (sPhysicalFilePath.EndsWith(".json") || sPhysicalFilePath.EndsWith(".csp"))
                            {
                                StringBuilder sbToFill = new StringBuilder();
                                ClassFromHTML cls = new ClassFromHTML(sPhysicalFilePath, sbToFill);
                                cls.execute(httpParameters);
                                iTotBytes = sbToFill.Length;
                                SendHeader(sHttpVersion, sMimeType, iTotBytes, " 200 OK", ref mySocket);
                                SendToBrowser(Encoding.ASCII.GetBytes(sbToFill.ToString()), ref mySocket);
                            }
                            else
                            {
                                FileStream fs = new FileStream(sPhysicalFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
                                // Create a reader that can read bytes from the FileStream.
                                BinaryReader reader = new BinaryReader(fs);
                                byte[] bytes = new byte[fs.Length];
                                int read;
                                while ((read = reader.Read(bytes, 0, bytes.Length)) > 0)
                                {
                                    // Read from the file and write the data to the network
                                    sResponse = sResponse + Encoding.ASCII.GetString(bytes, 0, read);

                                    iTotBytes = iTotBytes + read;

                                }
                                reader.Close();
                                fs.Close();

                                SendHeader(sHttpVersion, sMimeType, iTotBytes, " 200 OK", ref mySocket);
                                SendToBrowser(bytes, ref mySocket);
                            }

                            //mySocket.Send(bytes, bytes.Length,0);

                        }
                    }
                    mySocket.Close();
                }
                catch (Exception ex)
                {
                }
            }
        }
    }
}
